package org.zch.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zch.common.utils.ListUtils;
import org.zch.dao.UserWorksDAO;
import org.zch.dao.WorksDAO;
import org.zch.entity.dto.BottomDTO;
import org.zch.entity.dto.WorksDTO;
import org.zch.entity.dto.WorksUserDTO;
import org.zch.entity.po.MessagePO;
import org.zch.entity.po.UserInfoPO;
import org.zch.entity.po.UserWorksPO;
import org.zch.entity.po.WorksPO;
import org.zch.entity.vo.UserInfoVO;
import org.zch.entity.vo.WorksByUserVO;
import org.zch.entity.vo.WorksVO;
import org.zch.service.*;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 作品表(Works)表服务实现类
 *
 * @author 刘新
 * @since 2024-01-03 14:09:06
 */
@Service("worksService")
public class WorksServiceImpl extends ServiceImpl<WorksDAO, WorksPO> implements WorksService {

    @Autowired
    private WorksDAO worksDAO;
    @Autowired
    private UserService userService;
    @Autowired
    private UserWorksDAO userWorksDAO;
    @Autowired
    private MessageService msgService;
    @Autowired
    private FileService fileService;
    @Autowired
    private RecommendService recommendService;
    @Autowired
    private RedisTemplate redisTemplate;

    // 垫底数据不会重复出现的时间（单位：分钟）
    Long timeout = 3L;


    /**
     * 新增作品
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insert(WorksPO worksPO) {
        worksPO.setCreateTime(new Date());
        worksPO.setStatus(0);
        worksPO.setLikeCount(0);
        worksPO.setScore(0);
        // 通过code查询userInfo
        UserInfoVO infoVO = userService.getByCode(worksPO.getUserCode());
        worksPO.setUserId(infoVO.getId());
        return worksDAO.insert(worksPO);
    }

    /**
     * 热门列表
     */
    @Override
    public List<WorksVO> hotList(WorksDTO page) {
        UserInfoVO userInfo = userService.getByCode(page.getUserCode());
        // 没有看过的 50%一级热门内容 + 20%二级 + 20%三级 + 10%四级
        int size = Math.toIntExact(page.getSize());
        Map<Integer, Integer> levelMap = getLevel(size);
        List<WorksVO> oneList = getWorks(userInfo.getId(),1, levelMap.get(1), 0);
        List<WorksVO> twoList = getWorks(userInfo.getId(), 2, levelMap.get(2), 0);
        List<WorksVO> threeList = getWorks(userInfo.getId(),3, levelMap.get(3), 0);
        List<WorksVO> fourList = getWorks(userInfo.getId(), 4, levelMap.get(4), 0);
        List<WorksVO> list = new ArrayList<>();
        list.addAll(oneList);
        list.addAll(twoList);
        list.addAll(threeList);
        list.addAll(fourList);
        // 垫底数据
        if (list.size() < 20) {
            Integer bottomSize = 20 - list.size();
            BottomDTO bottomDTO = new BottomDTO();
            bottomDTO.setSize(bottomSize);
            List<Integer> notList = list.stream().map(WorksPO::getId).collect(Collectors.toList());
            bottomDTO.setNotList(notList);
            List<WorksVO> bottomList = getBottom(page.getUserCode(),bottomDTO);
            list.addAll(bottomList);
        }
        return list;
    }

    /**
     * 最新列表
     */
    @Override
    public IPage<WorksVO> newList(WorksDTO page) {
        UserInfoVO userInfo = userService.getByCode(page.getUserCode());
        page.setUserId(userInfo.getId());
        IPage<WorksVO> iPage = this.worksDAO.newList(page);
        if (ArrayUtil.isAllNotEmpty(iPage)) {
            List<WorksVO> list = new ArrayList<>(iPage.getRecords());
            if (list.size() < 20) {
                Integer bottomSize = 20 - list.size();
                BottomDTO bottomDTO = new BottomDTO();
                bottomDTO.setSize(bottomSize);
                if (ArrayUtil.isAllNotEmpty(list)){
                    List<Integer> notList = list.stream().map(WorksPO::getId).collect(Collectors.toList());
                    bottomDTO.setNotList(notList);
                }
                List<WorksVO> bottom = getBottom(page.getUserCode(),bottomDTO);
                list.addAll(bottom);
                iPage.setRecords(list);
                iPage.setTotal(20);
            }
        }
        return iPage;
    }


    /**
     * 发现列表
     */
    @Override
    public IPage<WorksVO> foundList(WorksDTO page) {
        UserInfoVO userInfo = userService.getByCode(page.getUserCode());
        page.setUserId(userInfo.getId());
        // redis模糊查询key
        String key = "bloom#" + userInfo.getCode() + "#:*";
        List<Integer> bloomWorkIds = (List<Integer>) redisTemplate.opsForValue().get(key);
        if (ArrayUtil.isAllNotEmpty(bloomWorkIds)) {
            // 从redis中获取已经看过的作品ids
            page.setBloomWorkIds(bloomWorkIds);
        }
        // 查询作品
        IPage<WorksVO> iPage = this.worksDAO.foundList(page);
        if (ArrayUtil.isAllNotEmpty(iPage)) {
            List<WorksVO> list = new ArrayList<>(iPage.getRecords());
            if (list.size() < 20) {
                // 数据不足20条时，获取垫底数据
                Integer bottomSize = 20 - list.size();
                BottomDTO bottomDTO = new BottomDTO();
                bottomDTO.setSize(bottomSize);
                bottomDTO.setHaveImg(1);
                if (ArrayUtil.isAllNotEmpty(list)){
                    List<Integer> notList = list.stream().map(WorksPO::getId).collect(Collectors.toList());
                    bottomDTO.setNotList(notList);
                }
                List<WorksVO> bottom = getBottom(page.getUserCode(),bottomDTO);
                list.addAll(bottom);
                iPage.setRecords(list);
                iPage.setTotal(20);
            }
        }
        // 将看过的作品添加到过滤器
        if (ArrayUtil.isAllNotEmpty(iPage)) {
            List<WorksVO> list = new ArrayList<>(iPage.getRecords());
            if (ArrayUtil.isAllNotEmpty(list)) {
                List<Integer> ids = list.stream().map(WorksPO::getId).collect(Collectors.toList());
                addFilter(userInfo.getCode(), ids);
            }
        }
        return iPage;
    }


    /**
     * 获取每个级别的条数
     */
    public Map<Integer, Integer> getLevel(int size) {
        double oneD = size * 0.5;
        double twoD = size * 0.2;
        double threeD = size * 0.2;
        double fourD = size * 0.1;
        int one = Math.toIntExact(Math.round(oneD));
        int two = Math.toIntExact(Math.round(twoD));
        int three = Math.toIntExact(Math.round(threeD));
        int four = Math.toIntExact(Math.round(fourD));
        int sum = one + two + three + four;
        // 数字对齐，防止条数不足或多余
        if (size > sum) {
            one = one + (size - sum);
        } else if (size < sum) {
            int c = sum - size;
            if (four >= c) {
                four = four - c;
            } else if (three >= c) {
                three = three - c;
            } else if (two >= c) {
                two = two - c;
            } else {
                one = one - c;
            }
        }
        // 返回数据
        Map<Integer, Integer> map = new HashMap<>();
        map.put(1, one);
        map.put(2, two);
        map.put(3, three);
        map.put(4, four);
        return map;
    }


    /**
     * 获取内容
     * @param userId 用户id
     * @param level  级别
     * @param size   条数
     * @param looked 是否看过 0否1是
     */
    public List<WorksVO> getWorks(Integer userId, Integer level, int size, Integer looked) {
        Map<String, Double> score = this.worksDAO.getScore();
        double max = score.get("max");
        double avg = score.get("avg");
        double min = score.get("min");
        double x = (max + avg) / 2;
        double y = (min + avg) / 2;
        double upper = 100;
        double lower = 0;
        if (level == 1) {
            // 一级：最大值 >= 评分 > x    其中x = (平均值 + 最大值)/2
            upper = max;
            lower = x;
        } else if (level == 2) {
            // 二级：x     >= 评分 > 中间值
            upper = x;
            lower = avg;
        } else if (level == 3) {
            // 三级：中间值 >= 评分 > y    其中y = (平均值 + 最小值)/2
            upper = avg;
            lower = y;
        } else if (level == 4) {
            // y     >= 评分 > 最小值
            upper = y;
            lower = min;
        }
        return this.worksDAO.getWorks(userId,upper, lower, looked, size);
    }


    /**
     * 待审核列表
     */
    @Override
    public IPage<WorksPO> awaitList(Page<WorksPO> page) {
        return this.worksDAO.waitList(page);
    }

    /**
     * 根据id获取作品数量
     *
     * @param userCode 用户code
     */
    @Override
    public WorksByUserVO getWorksQuantityByUserCode(String userCode) {
        WorksByUserVO vo = new WorksByUserVO();
        Integer awaitNum = this.worksDAO.getWorksQuantityByUserCode(userCode, 0);
        Integer passNum = this.worksDAO.getWorksQuantityByUserCode(userCode, 1);
        Integer refuseNum = this.worksDAO.getWorksQuantityByUserCode(userCode, 2);
        vo.setAwaitNum(awaitNum);
        vo.setPassNum(passNum);
        vo.setRefuseNum(refuseNum);
        return vo;
    }

    /**
     * 根据code、状态获取用户作品
     *
     * @param page 查询对象
     * @return 返回结果
     */
    @Override
    public IPage<WorksVO> getWorksByUserCode(WorksUserDTO page) {
        //获取查看用户的id
        /*UserInfoVO userInfo = userService.getByCode(page.getUserCode());
        if (null != userInfo){
            page.setUserId(userInfo.getId());
        }*/
        //获取登陆用户的id
        UserInfoVO nowUserInfo = userService.getByCode(page.getNowUserCode());
        if (null != nowUserInfo){
            page.setNowUserId(nowUserInfo.getId());
        }
        return this.worksDAO.getWorksByUserCode(page);
    }


    /**
     * 批量更新是否看过字段
     *
     * @param map ids:作品ids
     *            userCode:用户编码
     * @return 更新的条数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer updateLooked(Map<String, Object> map) {
        // 获取数据
        List<Integer> ids = Convert.convert(List.class, map.get("ids"));
        String userCode = map.get("userCode").toString();
        // 用户数据
        UserInfoVO infoVO = userService.getByCode(userCode);
        // 组装数据进行批量插入或更新
        List<UserWorksPO> list = new ArrayList<>();
        for (Integer id : ids) {
            UserWorksPO userWorksPO = new UserWorksPO();
            userWorksPO.setWorksId(id);
            userWorksPO.setUserId(infoVO.getId());
            userWorksPO.setLooked(1);
            list.add(userWorksPO);
        }
        int i = this.saveOrUpdateUserWorks(list);
        // 增加作品浏览量
        worksDAO.addPlayCount(ids);
        return i;
    }

    /**
     * 根据作品id进行删除
     *
     * @param id 作品id
     * @return 操作的条数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteWork(Integer id) {
        // 删除作品相关文件
        WorksPO worksPO = this.getById(id);
        if (null != worksPO.getPictureUrl() && !"".equals(worksPO.getPictureUrl())){
            fileService.deleteByName(worksPO.getPictureUrl());
        }
        if (null != worksPO.getVoiceUrl() && !"".equals(worksPO.getVoiceUrl())){
            fileService.deleteByName(worksPO.getVoiceUrl());
        }
        // 删除作品
        int i = worksDAO.deleteById(id);
        // 删除用户作品表数据
        userWorksDAO.deleteByWorksId(id);
        // 删除推荐表数据
        recommendService.deleteByWorksId(id);
        return i;
    }

    /**
     * 点赞作品
     *
     * @param worksPO 用户code、作品id
     * @return 操作的条数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer likeWorks(WorksPO worksPO) {
        //获取用户信息
        UserInfoVO infoVO = userService.getByCode(worksPO.getUserCode());
        //拿到当前作品数据
        WorksPO msgWork = this.getById(worksPO.getId());
        // 点赞作品
        UserWorksPO userWorksPO = new UserWorksPO();
        userWorksPO.setWorksId(worksPO.getId());
        userWorksPO.setUserId(infoVO.getId());
        userWorksPO.setLiked(1);
        int i = this.saveOrUpdateUserWorks(Collections.singletonList(userWorksPO));
        // 增加点赞量
        worksDAO.addLikedCount(Collections.singletonList(worksPO.getId()));
        // 点赞人不是自己时，发送消息
        if (!infoVO.getCode().equals(msgWork.getUserCode())) {
            MessagePO msgPO = new MessagePO();
            msgPO.setMsgContent(infoVO.getName() + "赞了作品:" + msgWork.getContent());
            msgPO.setSendUserId(infoVO.getId());
            msgPO.setSendUserCode(infoVO.getCode());
            msgPO.setWorkId(msgWork.getId());
            msgPO.setType("JUMP_INDEX");
            msgPO.setValue(msgWork.getId().toString());
            msgPO.setToUserId(msgWork.getUserId());
            msgPO.setToUserCode(msgWork.getUserCode());
            msgPO.setRelateId(msgWork.getId());
            msgPO.setSource(0);
            msgService.sendMsg(msgPO);
        }
        return i;
    }


    /**
     * 批量更新或保存 关联表信息
     * 调用方法前，需要确保 作品id、用户id不为null
     *
     * @param list 用户id、作品id
     * @return 操作条数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveOrUpdateUserWorks(List<UserWorksPO> list) {
        List<Integer> worksIds = list.stream().map(UserWorksPO::getWorksId).collect(Collectors.toList());
        List<UserWorksPO> getList = userWorksDAO.getInfoByWorksId(worksIds);
        Map<String, List<UserWorksPO>> listMap = getList.stream().collect(Collectors.groupingBy(x -> x.getWorksId() + "#" + x.getUserId()));
        // 组装数据
        List<UserWorksPO> updateList = new ArrayList<>();
        List<UserWorksPO> insertList = new ArrayList<>();
        for (UserWorksPO userWorksPO : list) {
            List<UserWorksPO> pos = listMap.get(userWorksPO.getWorksId() + "#" + userWorksPO.getUserId());
            if (ArrayUtil.isAllNotEmpty(pos)) {
                // 更新
                Integer id = pos.get(0).getId();
                userWorksPO.setId(id);
                updateList.add(userWorksPO);
            } else {
                // 插入
                userWorksPO.setLooked(1);
                userWorksPO.setBeenPlayed(0);
                insertList.add(userWorksPO);
            }
        }
        int s = 0;
        if (ArrayUtil.isAllNotEmpty(updateList)) {
            int u = userWorksDAO.batchUpdate(updateList);
            s = s + u;
        }
        if (ArrayUtil.isAllNotEmpty(insertList)) {
            int i = userWorksDAO.insertBatch(insertList);
            s = s + i;
        }
        return s;
    }

    /**
     * 取消点赞
     *
     * @param worksPO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer noLikeWorks(WorksPO worksPO) {
        UserInfoVO infoVO = userService.getByCode(worksPO.getUserCode());
        // 取消点赞
        UserWorksPO userWorksPO = new UserWorksPO();
        userWorksPO.setWorksId(worksPO.getId());
        userWorksPO.setUserId(infoVO.getId());
        userWorksPO.setLiked(0);
        int i = this.saveOrUpdateUserWorks(Collections.singletonList(userWorksPO));
        // 减少点赞量
        worksDAO.reduceLikedCount(Collections.singletonList(worksPO.getId()));
        return i;
    }

    /**
     * 点赞列表
     *
     * @param dto 查询条件
     * @return 分页对象
     */
    @Override
    public IPage<WorksVO> likeList(WorksDTO dto) {
        return this.userWorksDAO.likeList(dto);
    }

    /**
     * 播放作品的音频
     *
     * @param worksPO 用户code、作品id
     * @return 操作的条数
     */
    @Override
    public Integer playWorks(WorksPO worksPO) {
        /*UserInfoVO infoVO = userService.getByCode(worksPO.getUserCode());
        // 播放作品
        UserWorksPO userWorksPO = new UserWorksPO();
        userWorksPO.setWorksId(worksPO.getId());
        userWorksPO.setUserId(infoVO.getId());
        //这里是设置作品已看，如果已看APP上默认就直接播放/查看了
        //前期默认都未看
        userWorksPO.setBeenPlayed(0);
        int i = this.saveOrUpdateUserWorks(Collections.singletonList(userWorksPO));*/
        //扣唠额
        UserInfoPO updatePO = new UserInfoPO();
        updatePO.setLaoNum(-1);
        updatePO.setCode(worksPO.getUserCode());
        int i = userService.setUserLaoNumByCode(updatePO);
        // 增加播放量
        worksDAO.addVoiceCount(Collections.singletonList(worksPO.getId()));
        return i;
    }


    /**
     * 垫底数据
     *
     * @param dto 查询的条数
     * @return 返回数据
     */
    public List<WorksVO> bottomList(BottomDTO dto) {
        if (null == dto.getSize()) {
            dto.setSize(5);
        }
        return worksDAO.bottomList(dto);
    }

    /**
     * 推荐垫底数据列表
     */
    @Override
    public IPage<WorksVO> recommendEmList(Page<WorksPO> page) {
        return  this.worksDAO.recommendEmList(page);
    }



    /**
     * 获取垫底数据
     *
     * @param userCode 用户编码
     * @param bottomDTO 查询条件
     * @return
     */
    @Override
    public List<WorksVO> getBottom(String userCode, BottomDTO bottomDTO) {
        // 用户信息
        UserInfoVO userInfo = userService.getByCode(userCode);
        BottomDTO beginDTO = new BottomDTO();
        BeanUtils.copyProperties(bottomDTO, beginDTO);
        // 获取redis列表中所有元素
        String now = DateUtil.now();
        String keys = "bottomIds" + userCode;
        Set<String> keySet = redisTemplate.keys(keys + "*");
        List<Integer> worksNotIds = new ArrayList<>();
        if (keySet != null) {
            for (String key : keySet) {
                if (key.startsWith(keys)) {
                    List<Integer> range = redisTemplate.opsForList().range(key, 0, -1);
                    if (range != null) {
                        worksNotIds.addAll(range);
                    }
                }
            }
        }
        // 计算需要查询的条数
        Integer bottomSize = bottomDTO.getSize();
        // 需要排除的作品ids
        if (!worksNotIds.isEmpty()) {
            List<Integer> notList = bottomDTO.getNotList();
            if (null == notList) {
                notList = new ArrayList<>();
            }
            notList.addAll(worksNotIds);
            bottomDTO.setNotList(notList);
        }
        // 查询数据，并放入redis中，设置timeout的过期时间
        bottomDTO.setSize(bottomSize * 10);
        if (null != userInfo && StringUtils.isNotBlank(userCode)){
            bottomDTO.setUserId(userInfo.getId());
        }
        List<WorksVO> bottomList = this.bottomList(bottomDTO);
        // 不传userCode时，没登陆，是否点赞置为false
        if (StringUtils.isBlank(userCode)) {
            for (WorksVO worksVO : bottomList) {
                worksVO.setLiked(false);
            }
        }
        List<WorksVO> randomSelection;
        if (bottomList.size() > bottomSize) {
            randomSelection = ListUtils.getRandomSelection(bottomList, bottomSize);
        } else {
            randomSelection = bottomList;
        }
        List<Integer> bottomIds = randomSelection.stream().map(WorksPO::getId).collect(Collectors.toList());
        if (bottomIds.isEmpty()) {
            if (keySet != null && !keySet.isEmpty()) {
                // 批量删除匹配的key
                for (String key : keySet) {
                    redisTemplate.delete(key);
                }
                randomSelection = getBottom(userCode, beginDTO);
            }
        } else {
            redisTemplate.opsForList().rightPushAll(keys + now, bottomIds);
            redisTemplate.expire(keys + now, timeout, TimeUnit.MINUTES);
        }
        return randomSelection;
    }

    /**
     * 获取所有作品的点赞数*/
    @Override
    public  Integer beenLikedNum(@Param("code") String code){
        return  this.worksDAO.beenLikedNum(code);
    }

    /**
     * 被播放音频数量
     * @param code 用户code
     * @return
     */
    @Override
    public Integer beenVoiceNum(@Param("code") String code){
        return  this.worksDAO.beenVoiceNum(code);
    }


    /**
     * 将已经看过的作品添加到过滤器中
     *
     * @param userCode 用户号
     * @param ids      作品ids
     * @return
     */
    @Override
    public Boolean addFilter(String userCode, List<Integer> ids) {
        // 布隆过滤器的key：bloom#用户code#当前时间戳
        String now = DateUtil.now();
        String keys = "bloom#" + userCode + "#" + now;
        redisTemplate.opsForList().rightPush(keys, ids);
        // 设置过期时间
        Boolean result = redisTemplate.expire(keys, timeout, TimeUnit.MINUTES);
        return result;
    }


    /**
     * 随机更新作品发布时间
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void randomUpdateWorkTime() {
        // 按照条件，查询作品（有图片、点赞数<5 ）
        List<WorksPO> worksPOList = this.worksDAO.oldWorks();
        // 生成随机数，随机数范围[5,15]
        Random random = new Random();
        int min = 5;
        int max = 15;
        int randomNumber = random.nextInt(max - min + 1) + min;
        // 随机取 randomNumber 条数据
        worksPOList = ListUtils.getRandomSelection(worksPOList, randomNumber);
        // 更新作品发布时间
        if (worksPOList.size() > 15) {
            return;
        }
        worksPOList.forEach(worksPO -> {
            worksPO.setCreateTime(DateUtil.parse(DateUtil.now()));
            this.worksDAO.updateById(worksPO);
        });

    }

}

